/*
	http://stackoverflow.com/questions/5221278/attempting-asynchronous-i-o-with-win32-threads
	http://blogs.msdn.com/b/oldnewthing/archive/2010/12/17/10106259.aspx
	http://stackoverflow.com/questions/12583482/serial-asynchronous-i-o-in-windows-7-64
	http://msdn.microsoft.com/en-us/library/windows/desktop/ms686915(v=vs.85).aspx
*/
#include "ThreadedComport.h"


ThreadedComport::ThreadedComport(void){
	_hCom = INVALID_HANDLE_VALUE;
	_isOpen = FALSE;
	fKeepReading = TRUE;
	notifyTo = NULL;
}


ThreadedComport::~ThreadedComport(void)
{
	close();
}

void ThreadedComport::setNotifyTo(HWND hwnd){
	notifyTo = hwnd;
}

//BOOL ThreadedComport::open(LPWSTR portName, DWORD baudrate, BYTE byteSize, BYTE parity)
BOOL ThreadedComport::open(LPWSTR portName, DWORD baudrate, BYTE byteSize, BYTE parity)
{
	this->_hCom = CreateFile(portName,
		GENERIC_READ | GENERIC_WRITE,
		NULL, // Dont want to share
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		0);

	if(_hCom == INVALID_HANDLE_VALUE)
		return FALSE;

	//Set timeout for comport
	COMMTIMEOUTS cto_old = {0}, cto_new;
	GetCommTimeouts(_hCom, &cto_old);
	cto_new.ReadTotalTimeoutConstant = 10;
	cto_new.ReadTotalTimeoutMultiplier = 0;
	cto_new.WriteTotalTimeoutConstant = 0;
	cto_new.WriteTotalTimeoutMultiplier = 0;

	if(SetCommTimeouts(_hCom, &cto_new) == FALSE)
	{
		CloseHandle(_hCom);
		return FALSE;
	}

	memset(&dcb, 0, sizeof(dcb));
	dcb.DCBlength = sizeof(dcb);
	if(!GetCommState(_hCom, &dcb))
	{
		CloseHandle(_hCom);
		return FALSE;
	}

	dcb.BaudRate = baudrate;
	dcb.ByteSize = byteSize;
	dcb.Parity = parity;
	dcb.StopBits = ONESTOPBIT;
	dcb.fInX = FALSE;

	if(!SetCommState(_hCom, &dcb))
	{
		CloseHandle(_hCom);
		return FALSE;
	}

	//this->_send_overlapped.o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	_recieve_overlapped.caller = this;
	

	_isOpen = TRUE;
	return TRUE;

}

BOOL ThreadedComport::close()
{
	if(_isOpen)
	{
		stopReading();
		CloseHandle(_hCom);
		_isOpen = FALSE;
	}

	return TRUE;
}

void ThreadedComport::stopReading()
{
	this->fKeepReading = FALSE;
}

void ThreadedComport::send_data()
{
	BOOL result;

	/*	Truoc khi thu hien mot request gui du lieu, can phai kiem tra xem co ai dang lam viec tren
		tren write_buff hay khong, neu co thi phai cho(wait) cho den khi no hoan tat cong viec do
	*/
	WaitForSingleObject(this->write_mutex, 1000);

	if(wlen <= 0)
		SetEvent(this->_hEventWriteDone);

	/*
		Gui bat dong bo du lieu trong write_buff, sau khi gui xong se call den ham callback
		_send_callback
	*/
	result = WriteFileEx(
		_hCom, this->write_buff, wlen,
		(LPOVERLAPPED)&_send_overlapped,
		(LPOVERLAPPED_COMPLETION_ROUTINE)&_send_callback);
}

void ThreadedComport::recieve_data(){

	if(HasOverlappedIoCompleted((OVERLAPPED*)&(this->_recieve_overlapped)))
	{
		/*	
			A pipe is, conceptually, an infinite stream of data. 
			ReadFile simply waits for the client to send more. 
		
		if(WaitForSingleObjectEx(_recieve_overlapped.o.hEvent, INFINITE, TRUE)==WAIT_OBJECT_0){
			ReadFileEx(_hCom,
				this->read_buff, READ_BUFF_LEN,
				(OVERLAPPED*)&(this->_recieve_overlapped),
				(LPOVERLAPPED_COMPLETION_ROUTINE)&_receive_callback);
		}*/
	}
}

VOID WINAPI ThreadedComport::recieve_thread_routine(PVOID param)
{
	DWORD res;
	int errorCount = 0;
	TCHAR msg[64];
	ThreadedComport* _this = (ThreadedComport*)param;

	/* Create the overlapped event */
	_this->_recieve_overlapped.o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if(_this->_recieve_overlapped.o.hEvent == NULL)
		return;

	_this->_recieve_overlapped.o.Internal = 0;
	_this->_recieve_overlapped.o.InternalHigh = 0;
	_this->_recieve_overlapped.o.Offset = 0;
	_this->_recieve_overlapped.o.OffsetHigh = 0;

	_this->fWaittingOnRead = FALSE;	

	while(_this->fKeepReading )
	{
		if( !(_this->fWaittingOnRead) )
		{
			if(errorCount > 100){
				//too many error occurred
				_this->fKeepReading = FALSE;
			}

			if(!ReadFile(_this->_hCom, _this->read_buff, READ_BUFF_LEN, &(_this->rlen), &(_this->_recieve_overlapped.o))){
				if( GetLastError() == ERROR_IO_PENDING ){
					_this->fWaittingOnRead = TRUE;	
				}
				else{
					errorCount++;
				}
			}
			else{ //Read OK
				if(_this->rlen > 0){
					//OK, do something with _this->read_buff
					_this->read_buff[_this->rlen] = 0;
					wsprintf(msg, L"ONE: %hs", _this->read_buff);
					//SendMessage(_this->notifyTo, WM_USER + 11/*SB_SETTEXT*/, 1, (LPARAM)msg);
				}
			}
		}

		if(_this->fWaittingOnRead){
			res = WaitForSingleObject(_this->_recieve_overlapped.o.hEvent, 100000);
			switch(res){
				case WAIT_OBJECT_0:
					if( !GetOverlappedResult(_this->_hCom, &(_this->_recieve_overlapped.o), &(_this->rlen), FALSE) ){
						//Error, do something
					}
					else{
						if(_this->rlen > 0){
							//OK, do something with _this->read_buff
							_this->read_buff[_this->rlen] = 0;
							//wsprintf(msg, L"TWO: %hs", _this->read_buff);
							wsprintf(msg, L"TWO: %d", *((int*)(_this->read_buff)));
							//SendMessage(_this->notifyTo, WM_USER + 11/*SB_SETTEXT*/, 1, (LPARAM)msg);

							//send to wave viewer
							SendMessage(_this->notifyTo, WM_USER + 3, 0, (LPARAM)(_this->read_buff) );
						}

						_this->fWaittingOnRead = FALSE;
					}
				break;

				case WAIT_TIMEOUT:
				default:
				break;
			}
		}
	}

	_this->fKeepReading = FALSE;
	CloseHandle(_this->_recieve_overlapped.o.hEvent);
}

void WINAPI ThreadedComport::_send_callback(DWORD _dwErrorCode, DWORD dwNumberOfByte, LPOVERLAPPED lpOverlapped){

	ThreadedComport* _this = ((OVERLAPPEDEX*)lpOverlapped)->caller;

	/*Neu khong co loi xay ra trong qua trinh gui du lieu*/
	if(_dwErrorCode == 0) {

	}

	/*Reset write_data buffer*/
	_this->wlen = 0;
	//SetEvent(lpOverlapped->hEvent);
}
